package com.lqg.bookLibrary.common.securityImpl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.json.JacksonJsonParser;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;

@Slf4j
public class MyUsernamePasswordFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        try {
            String bodyString = getBodyString(request);
            if (!StringUtils.hasText(bodyString)) {
                throw new IllegalArgumentException("用户名或密码不能为空");
            }
            Map<String, Object> map = new JacksonJsonParser().parseMap(bodyString);

            if (!request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException("不支持该方法: " + request.getMethod());
            }
            String username = obtainUsername(map);
            username = username.trim();
            if (!StringUtils.hasText(username)) {
                throw new UsernameNotFoundException("用户名为空");
            }
            String password = obtainPassword(map);
            if (!StringUtils.hasText(password)) {
                throw new UsernameNotFoundException("密码为空");
            }
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        } catch (IOException e) {
            log.error("解析登录参数时发生异常,{}", e.getMessage());
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }

    private String obtainUsername(Map<String, Object> map){
        Object userName = map.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY);
        return userName == null ? "" : (String)userName;
    }

    private String obtainPassword(Map<String, Object> map){
        Object pwd = map.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY);
        return pwd == null ? "" : (String)pwd;
    }

    /**
     * 接收传递的jsonBody类型的参数
     */
    private String getBodyString(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(
                    new InputStreamReader(inputStream, StandardCharsets.UTF_8));

            char[] bodyCharBuffer = new char[1024];
            int len = 0;
            while ((len = reader.read(bodyCharBuffer)) != -1) {
                sb.append(new String(bodyCharBuffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

